zhouqijie

一、表面着色器简介:

表面着色器时在顶点/片段着色器上添加的一层抽象。
把渲染流程划分为表面着色器、光照模型、光照着色器。光照模型是预定义的,光照着色器由系统实现,我们只需关注表面着色器(纹理颜色法线等),大大了减轻工作量。

二、编译指令:

编译指令最重要的作用是指明着色器使用的表面函数和光照函数,并设置一些可选参数。编译指令往往写在CG块的第一句。
格式:#pragma surface <surface func> <light model> [optional params]

表面函数

表面函数用于定义反射率、光滑度、透明度等表面属性。
函数格式:void surf(Input IN, inout <stuct> o)
SurfaceOutput、SurfaceOutputStandard、SurfaceOutputStandardSpecular都是Unity内置的结构体,后两个是Unity5加入物理渲染后新增的。

光照函数

光照函数会使用表面函数中设置的各种属性来应用光照模型。
Unity内置了基于物理的光照模型函数Standard和StandardSpecular,以及Lambert和BlinnPhong。

自定义光照函数:(Unity手册)
half4 Lighting<Name>(SurfaceOutput s, half3 lightDir, half atten)
half4 Lighting<Name>(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)

三、结构体属性声明

Input结构体中的变量:

float3 viewDir 视角方向。
float4 COLOR 插值后的逐顶点颜色。
float4 screenPos屏幕空间坐标。
float3 worldPos世界空间位置。
float3 worldRefl世界空间反射。
float3 worldNormal世界空间法线。

不需要计算这些变量,只需在Input定义中按名称声明它们。
如果修改了表面法线o.Normal,需要告诉Unity要基于修改后的法线计算世界空间反射方向和法线方向。

Output结构体属性:

SurfaceOutput、SurfaceOutputStandard、SurfaceOutputStandardSpecular的声明可以在Lighting.cginc和UnityPBSLighting.cginc中找到:

//
struct SurfaceOutput
{
    fixed3 Albedo;      //光源反射率。(纹理采样和颜色属性乘积)
    fixed3 Normal;      //法线方向。
    fixed3 Emission;    //自发光。
    half Specular;      //高光反射指数部分系数。
    fixed Gloss;        //高光反射强度系数。
    fixed Alpha;        //透明通道。
}
//
stuct SurfaceOutputStandard
{
    fixed3 Albedo;
    fixed3 Normal;
    fixed3 Emission;
    half Metallic;
    half Smoothness;
    half Occlusion;
    fixed Alpha;
}//
stuct SurfaceOutputStandardSpecular
{
    fixed3 Albedo;
    fixed3 Specular;
    fixed3 Normal;
    fixed3 Emission;
    half Smoothness;
    half Occlusion;
    fixed Alpha;
}

四、表面着色器底层原理

Unity会根据表面着色器生成一个包含很多Pass的顶点/片段着色器。

补充:可选参数:

透明度和 Alpha 测试:

alphaalpha:auto - 对于简单的光照函数,将选择淡化透明度(与 alpha:fade 相同);对于基于物理的光照函数,将选择预乘透明度(与 alpha:premul 相同)。
alpha:blend - 启用 Alpha 混合。
alpha:fade - 启用传统淡化透明度。
alpha:premul - 启用预乘 Alpha 透明度。
alphatest:VariableName - 启用 Alpha 镂空透明度。剪切值位于具有 VariableName 的浮点变量中。您可能还想使用 addshadow 指令生成正确的阴影投射物通道。
keepalpha - 默认情况下,无论输出结构的 Alpha 输出是什么,或者光照函数返回什么,不透明表面着色器都将 1.0(白色)写入 Alpha 通道。使用此选项可以保持光照函数的 Alpha 值,即使对于不透明的表面着色器也是如此。
decal:add - 附加贴花着色器(例如 terrain AddPass)。这适用于位于其他表面之上并使用附加混合的对象。
decal:blend - 半透明贴花着色器。这适用于位于其他表面之上并使用 Alpha 混合的对象。

自定义修改器函数:

vertex:VertexFunction - 自定义顶点修改函数。在生成的顶点着色器的开始处调用此函数,并且此函数可以修改或计算每顶点数据。请参阅表面着色器示例。
finalcolor:ColorFunction - 自定义最终颜色修改函数。请参阅表面着色器示例。
finalgbuffer:ColorFunction - 用于更改 G 缓冲区内容的自定义延迟路径。
finalprepass:ColorFunction - 自定义预通道基本路径。

阴影和曲面细分:

addshadow - 生成阴影投射物通道。常用于自定义的顶点修改,以便阴影投射也可以获得程序化顶点动画。通常情况下,着色器不需要任何特殊的阴影处理,因为它们可以通过回退机制来使用阴影投射物通道。
fullforwardshadows - 支持前向渲染路径中的所有光源阴影类型。默认情况下,着色器仅支持前向渲染中来自一个方向光的阴影(以节省内部着色器变体数量)。如果在前向渲染中需要点光源阴影或聚光灯阴影,请使用此指令。
tessellate:TessFunction - 使用 DX11 GPU 曲面细分;该函数计算曲面细分因子。有关详细信息,请参阅表面着色器曲面细分。

代码生成选项:

exclude_path:deferred、exclude_path:forward 和 exclude_path:prepass - 不为给定的渲染路径(分别对应延迟着色路径、前向路径和旧版延迟路径)生成通道。
noshadow - 禁用此着色器中的所有阴影接受支持。
noambient - 不应用任何环境光照或光照探针。
novertexlights - 在前向渲染中不应用任何光照探针或每顶点光源。
nolightmap - 禁用此着色器中的所有光照贴图支持。
nodynlightmap - 禁用此着色器中的运行时动态全局光照支持。
nodirlightmap - 禁用此着色器中的方向光照贴图支持。
nofog - 禁用所有内置雾效支持。
nometa - 不生成“Meta”通道(由光照贴图和动态全局光照用于提取表面信息)。
noforwardadd - 禁用前向渲染附加通道。这会使着色器支持一个完整方向光,所有其他光源均进行每顶点/SH 计算。也能减小着色器。
nolppv - 禁用此着色器中的光照探针代理体支持。
noshadowmask - 为此着色器禁用阴影遮罩支持(包括 Shadowmask 和 Distance Shadowmask)。

其他:

softvegetation - 仅在开启 Soft Vegetation 时才渲染表面着色器。
interpolateview - 在顶点着色器中计算视图方向并进行插值;而不是在像素着色器中计算。这可以使像素着色器更快,但会额外消耗一个纹理插值器。
halfasview - 将半方向矢量传入光照函数而不是视图方向。计算半方向并按每个顶点对其进行标准化。这更快,但并不完全正确。
approxview - 在 Unity 5.0 中已删除。请改用 interpolateview。
dualforward - 在前向渲染路径中使用双光照贴图。
dithercrossfade - 使表面着色器支持抖动效果。然后,可将此着色器应用于使用细节级别组 (LOD Group) 组件(配置为交叉淡入淡出过渡模式)的游戏对象。

注意事项:

一. 多PASS

在surface shader中,不能用pass关键字。但是最终的shader生成后,会自己生成pass。如果想在Surface shader中写多个pass只需把pass的内容包含到CGPROGRAM/ENDCG之中。 实例:

//...
Cull Front
CGPROGRAM
//...
ENDCG
Cull Back
CGPROGRAM
//...
ENDCG
//...

二. 关键字和可选参数冲突

Surface shader的pragma可选参数可能会覆盖ZWrite ZTest。使用keepalpha可选参数可使ZTest/Zwirte为开启。可以用于由不透明物体的变透明渐出效果。